/**
 * @file exercise_record.c
 *
 */

/*********************
*      INCLUDES
*********************/
#include "exercise_record.h"

#if USE_LV_WATCH_EXERCISE_RECORD != 0

/*********************
*      DEFINES
*********************/
#define EXERCISE_RECORD_NV_PATH_PREFIX             ("C:/ui_exercise_record_")
#define EXERCISE_RECORD_NV_PATH_SUFFIX             (".nv")
                                                   
#define EXERCISE_RECORD_MAX_NUM                    (10)

#define EXERCISE_RECORD_TRANING_EFFECT_ARC_ANGLE   (136)

#define EXERCISE_RECORD_VO2MAX_ARC_INTERVAL_ANGLE  (1)
#define EXERCISE_RECORD_VO2MAX_ONE_ARC_ANGLE       (38)

/**********************
*      TYPEDEFS
**********************/
enum {
    EXERCISE_RECORD_DATA_TYPE_DISTANCE = 0,
    EXERCISE_RECORD_DATA_TYPE_CALORIE,
};
typedef uint8_t EXERCISE_RECORD_DATA_TYPE_T;

typedef struct {
    uint8_t sport_type;
    EXERCISE_RECORD_DATA_TYPE_T data_type;
    uint16_t padding;              // unused
    uint32_t data;                 // in meters for distance, in kcal for calorie
    uint32_t end_time;             // in seconds
} exercise_record_nv_data_t;

typedef struct {
    exercise_record_nv_data_t data[EXERCISE_RECORD_MAX_NUM];
} exercise_record_nv_t;

typedef struct {
    uint16_t txt_id;
    char str[20];
    void * img_src;
} exercise_record_content_info_t;

typedef struct {
    uint16_t title_txt_id;
    exercise_record_content_info_t info[3];
} exercise_record_content_t;

/**********************
*  STATIC PROTOTYPES
**********************/

/**********************
*  STATIC VARIABLES
**********************/

/**********************
*      MACROS
**********************/

/**********************
 *   STATIC FUNCTIONS
 **********************/
static uint8_t *  exercise_record_read_nv(uint8_t nv_index, uint8_t * data)
{
    char path[30] = {};
    snprintf(path, 30, "%s%d%s", EXERCISE_RECORD_NV_PATH_PREFIX, nv_index,
             EXERCISE_RECORD_NV_PATH_SUFFIX);

    lv_fs_file_t file = {};
    lv_fs_open(&file, path, LV_FS_MODE_RD);
    if(file.file_d) {
        uint32_t size;
        lv_fs_size(&file, &size);
        if(0 < size) {
            if(NULL == data) data = Hal_Mem_Alloc(size);
            lv_fs_seek(&file, 0);
            lv_fs_read(&file, data, size, NULL);
        };
    } else {
        printf("%s: warning, nv %d is absent\n", __FUNCTION__, nv_index);
    }
    lv_fs_close(&file);

    return(data);
}

static void exercise_record_write_nv(uint8_t nv_index, uint32_t size, void * data)
{
    if((NULL == data) || (0 == size)) return;

    char path[30] = {};
    snprintf(path, 30, "%s%d%s", EXERCISE_RECORD_NV_PATH_PREFIX, nv_index,
             EXERCISE_RECORD_NV_PATH_SUFFIX);

    lv_fs_file_t file = {};
    lv_fs_open(&file, path, LV_FS_MODE_WR);
    lv_fs_write(&file, data, size, NULL);
    lv_fs_close(&file);
}

static void exercise_record_delete_nv(uint8_t nv_index)
{
    char path[30] = {};
    snprintf(path, 30, "%s%d%s", EXERCISE_RECORD_NV_PATH_PREFIX, nv_index,
             EXERCISE_RECORD_NV_PATH_SUFFIX);
    lv_fs_remove(path);
}

static void exercise_record_prepare_destroy(lv_obj_t * activity_obj)
{
    printf("%s\n", __FUNCTION__);
    lv_watch_png_cache_all_free();
}

static void exercise_record_close_cb(lv_obj_t * btn, lv_event_t event)
{
    if(LV_EVENT_CLICKED != event) return;

    lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_EXERCISE_RECORD_SPORT);
    if(NULL == activity_obj) return;

    lv_watch_activity_ext_t * ext = lv_obj_get_ext_attr(activity_obj);
    LV_ASSERT_MEM(ext);
    if(ext->prepare_destory) {
        ext->prepare_destory(activity_obj);
    }
    lv_obj_del(activity_obj);
}

static void exercise_record_add_exit(lv_obj_t * page)
{
    lv_coord_t obj_w = lv_obj_get_width(page);
    lv_coord_t obj_h = lv_obj_get_height(page);

    lv_obj_t * bg = lv_cont_create(page, NULL);
    lv_obj_set_style_local_bg_opa(bg, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0);
    lv_obj_set_size(bg, obj_w, obj_h);
    lv_obj_set_click(bg, false);

    lv_obj_t * img = lv_img_create(bg, NULL);
    lv_img_set_src(img, ICON_EXERCISE_RECORD_SPORTS_AND_HEALTH);
    lv_coord_t img_h = lv_obj_get_height(img);
    lv_img_set_zoom(img, LV_IMG_ZOOM_NONE * obj_h / (img_h * 5));
    lv_obj_align(img, bg, LV_ALIGN_IN_TOP_MID, 0, obj_h / 5 - img_h / 2);

    lv_obj_t * label = lv_label_create(bg, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
    lv_label_set_long_mode(label, LV_LABEL_LONG_BREAK);
    lv_obj_set_width(label, obj_w * 4 / 5);
    lv_label_set_text_id(label, WATCH_TEXT_ID_SHARE_RECORD);
    lv_obj_align(label, bg, LV_ALIGN_CENTER, 0, 0);

    lv_obj_t * btn = lv_btn_create(bg, NULL);
    lv_btn_set_layout(btn, LV_LAYOUT_OFF);
    lv_obj_set_size(btn, obj_w * 3, obj_w * 3);
    lv_obj_set_style_local_bg_opa(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50);
    lv_obj_set_style_local_bg_color(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_obj_set_style_local_radius(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
    lv_obj_set_style_local_bg_opa(btn, LV_BTN_PART_MAIN, LV_STATE_PRESSED, LV_OPA_50);
    lv_obj_set_style_local_bg_color(btn, LV_BTN_PART_MAIN, LV_STATE_PRESSED, LV_COLOR_GRAY);
    lv_obj_set_style_local_radius(btn, LV_BTN_PART_MAIN, LV_STATE_PRESSED, LV_RADIUS_CIRCLE);
    lv_watch_obj_add_element(btn);
    lv_obj_set_event_cb(btn, exercise_record_close_cb);
    lv_obj_align(btn, bg, LV_ALIGN_IN_TOP_MID, 0, obj_h * 4 / 5);

    label = lv_label_create(btn, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_label_set_text_id(label, WATCH_TEXT_ID_OFF);
    lv_obj_align(label, btn, LV_ALIGN_IN_TOP_MID, 0, (obj_h / 5 - lv_obj_get_height(label)) / 2);
}

static void exercise_record_add_vo2max(lv_obj_t * page, lv_coord_t arc_w,
                                       exercise_record_vo2max_t * vo2max)
{
    lv_coord_t obj_w = lv_obj_get_width(page);
    lv_coord_t obj_h = lv_obj_get_height(page);

    lv_obj_t * bg = lv_cont_create(page, NULL);
    lv_obj_set_style_local_bg_opa(bg, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0);
    lv_obj_set_size(bg, obj_w, obj_h);
    lv_obj_set_click(bg, false);

    lv_bidi_dir_t dir = lv_obj_get_base_dir(bg);

    lv_color_t color[EXERCISE_RECORD_VO2MAX_ZONE_NUM]
        = {LV_COLOR_MAKE(0xFF, 0x60, 0x00), LV_COLOR_ORANGE, LV_COLOR_MAKE(0xFF, 0xc0, 0X00),
           LV_COLOR_YELLOW, LV_COLOR_MAKE(0x00, 0xFF, 0x40), LV_COLOR_MAKE(0x00, 0xF0, 0xFF),
           LV_COLOR_MAKE(0x00, 0x80, 0xFF)};
    uint8_t i = 0;
    if(LV_BIDI_DIR_RTL == dir) {
        for(;i < EXERCISE_RECORD_VO2MAX_ZONE_NUM;i++) {
            if((EXERCISE_RECORD_VO2MAX_ZONE_NUM - 1 - i) == i) break;
            lv_color_t temp = color[i];
            color[i] = color[EXERCISE_RECORD_VO2MAX_ZONE_NUM - 1 - i];
            color[EXERCISE_RECORD_VO2MAX_ZONE_NUM - 1 - i] = temp;
        }
    }
    int32_t total_angle = EXERCISE_RECORD_VO2MAX_ONE_ARC_ANGLE * EXERCISE_RECORD_VO2MAX_ZONE_NUM +
        EXERCISE_RECORD_VO2MAX_ARC_INTERVAL_ANGLE * (EXERCISE_RECORD_VO2MAX_ZONE_NUM - 1);
    int32_t init_angle = 270 - total_angle / 2;
    int32_t start_angle = init_angle;
    lv_obj_t * arc[EXERCISE_RECORD_VO2MAX_ZONE_NUM];
    for(i = 0;i < EXERCISE_RECORD_VO2MAX_ZONE_NUM;i++) {
        arc[i] = lv_arc_create(bg, NULL);
        lv_obj_set_size(arc[i], obj_w, obj_h);
        lv_obj_align(arc[i], bg, LV_ALIGN_CENTER, 0, 0);
        if(360 <= start_angle) {
            start_angle -= 360;
        }
        lv_arc_set_bg_angles(arc[i], start_angle,
                             start_angle + EXERCISE_RECORD_VO2MAX_ONE_ARC_ANGLE);
        start_angle += EXERCISE_RECORD_VO2MAX_ONE_ARC_ANGLE +
            EXERCISE_RECORD_VO2MAX_ARC_INTERVAL_ANGLE;
        lv_obj_set_style_local_bg_opa(arc[i], LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_OPA_0);
        lv_obj_set_style_local_line_rounded(arc[i], LV_ARC_PART_BG, LV_STATE_DEFAULT, false);
        lv_obj_set_style_local_line_color(arc[i], LV_ARC_PART_BG, LV_STATE_DEFAULT, color[i]);
        lv_obj_set_style_local_line_opa(arc[i], LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_OPA_COVER);
        lv_obj_set_style_local_line_width(arc[i], LV_ARC_PART_BG, LV_STATE_DEFAULT, arc_w);
        lv_arc_set_range(arc[i], vo2max->zone[i], vo2max->zone[i + 1] - 1);
        lv_obj_set_click(arc[i], false);
    }

    for(i = 0;i < EXERCISE_RECORD_VO2MAX_ZONE_NUM;i++) {
        if(vo2max->value < vo2max->zone[i + 1]) {
            break;
        }
    }
    uint8_t vo2max_value = vo2max->value;
    if(EXERCISE_RECORD_VO2MAX_ZONE_NUM == i) {
        vo2max_value = vo2max->zone[i] - 1;
        i--;
    }
    lv_obj_t * img = lv_img_create(bg, NULL);
    lv_img_set_src(img, ICON_EXERCISE_TRIANGLE);
    lv_coord_t img_w = lv_obj_get_width(img);
    lv_img_set_zoom(img, LV_IMG_ZOOM_NONE * arc_w / img_w);
    // the distance from center to the left side of img
    lv_coord_t radius = obj_w / 2 - arc_w / 5 - arc_w / 2 - img_w / 2;
    lv_coord_t pivot_x = radius * img_w / arc_w; // radius * zoom
    lv_img_set_pivot(img, -pivot_x, lv_obj_get_height(img) / 2);
    lv_obj_align(img, bg, LV_ALIGN_IN_RIGHT_MID, radius * img_w / arc_w + img_w - obj_w / 2, 0);
    int32_t cur_angle;
    if(LV_BIDI_DIR_RTL == dir) {
        int32_t cur_end_angle = init_angle + EXERCISE_RECORD_VO2MAX_ONE_ARC_ANGLE +
            (EXERCISE_RECORD_VO2MAX_ONE_ARC_ANGLE + EXERCISE_RECORD_VO2MAX_ARC_INTERVAL_ANGLE) *
            (EXERCISE_RECORD_VO2MAX_ZONE_NUM - 1 - i);
        cur_angle = cur_end_angle * 10 - EXERCISE_RECORD_VO2MAX_ONE_ARC_ANGLE * 10 *
            (vo2max_value - vo2max->zone[i]) / (vo2max->zone[i + 1] - 1 - vo2max->zone[i]);
    } else {
        int32_t cur_start_angle = init_angle + (EXERCISE_RECORD_VO2MAX_ONE_ARC_ANGLE +
                                                EXERCISE_RECORD_VO2MAX_ARC_INTERVAL_ANGLE) * i;
        cur_angle = cur_start_angle * 10 + EXERCISE_RECORD_VO2MAX_ONE_ARC_ANGLE * 10 *
            (vo2max_value - vo2max->zone[i]) / (vo2max->zone[i + 1] - 1 - vo2max->zone[i]);
    }
    if(3600 <= cur_angle) {
        cur_angle -= 3600;
    }
    lv_img_set_angle(img, cur_angle);

    lv_obj_t * label = lv_label_create(bg, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
    lv_label_set_long_mode(label, LV_LABEL_LONG_SROLL_CIRC);
    lv_obj_set_width(label, obj_w * 2 / 3);
    lv_label_set_text_id(label, WATCH_TEXT_ID_MAX_OXYGEN_UPTAKE);
    lv_obj_align(label, bg, LV_ALIGN_IN_TOP_MID, 0, arc_w + obj_h / 10);

    label = lv_label_create(bg, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_BIG);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    char str[4] = {};
    snprintf(str, 4, "%d", vo2max->value);
    lv_label_set_text(label, str);
    lv_obj_align(label, bg, LV_ALIGN_CENTER, 0, 0);

    lv_obj_t * label1 = lv_label_create(bg, NULL);
    lv_obj_set_style_local_text_font(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_SMALL);
    lv_obj_set_style_local_text_letter_space(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_label_set_align(label1, LV_LABEL_ALIGN_CENTER);
    lv_obj_set_width(label1, obj_w * 2 / 3);
    switch(i) {
    case 0:
        lv_label_set_text_id(label1, WATCH_TEXT_ID_LOW_LEVEL);
        break;
    case 1:
        lv_label_set_text_id(label1, WATCH_TEXT_ID_AMATEUR_LEVEL);
        break;
    case 2:
        lv_label_set_text_id(label1, WATCH_TEXT_ID_GENERAL_LEVEL);
        break;
    case 3:
        lv_label_set_text_id(label1, WATCH_TEXT_ID_AVERAGE_LEVEL);
        break;
    case 4:
        lv_label_set_text_id(label1, WATCH_TEXT_ID_GOOD_LEVEL);
        break;
    case 5:
        lv_label_set_text_id(label1, WATCH_TEXT_ID_EXCELLENT_LEVEL);
        break;
    case 6:
        lv_label_set_text_id(label1, WATCH_TEXT_ID_PROFESSIONAL_LEVEL);
        break;
    }
    lv_obj_align(label1, bg, LV_ALIGN_IN_BOTTOM_MID, 0, -obj_h / 10);
    lv_coord_t bottom_y = lv_obj_get_y(label1);

    label1 = lv_label_create(bg, NULL);
    lv_obj_set_style_local_text_font(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_SMALL);
    lv_obj_set_style_local_text_letter_space(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_text_color(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_label_set_text(label1, "ml/kg/min");
    lv_obj_align(label1, label, LV_ALIGN_OUT_BOTTOM_MID, 0,
                 (bottom_y - lv_obj_get_y(label) - lv_obj_get_height(label) -
                  lv_obj_get_height(label1)) / 2);
}

static void exercise_record_add_recovery_time(lv_obj_t * page, lv_coord_t arc_w,
                                              exercise_record_recovery_time_t * recovery_time)
{
    lv_coord_t obj_w = lv_obj_get_width(page);
    lv_coord_t obj_h = lv_obj_get_height(page);

    lv_obj_t * bg = lv_cont_create(page, NULL);
    lv_obj_set_style_local_bg_opa(bg, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0);
    lv_obj_set_size(bg, obj_w, obj_h);
    lv_obj_set_click(bg, false);

    lv_obj_t * arc = lv_arc_create(bg, NULL);
    lv_obj_set_size(arc, obj_w, obj_h);
    lv_obj_set_style_local_bg_opa(arc, LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_OPA_0);
    lv_obj_set_style_local_line_rounded(arc, LV_ARC_PART_BG, LV_STATE_DEFAULT, false);
    lv_obj_set_style_local_line_color(arc, LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_obj_set_style_local_line_opa(arc, LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_OPA_50);
    lv_obj_set_style_local_line_width(arc, LV_ARC_PART_BG, LV_STATE_DEFAULT, arc_w);
    lv_obj_set_style_local_line_color(arc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT,
                                      LV_COLOR_MAKE(0x00, 0xFF, 0x40));
    lv_obj_set_style_local_line_opa(arc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_OPA_COVER);
    lv_obj_set_style_local_line_rounded(arc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, false);
    lv_obj_set_style_local_line_width(arc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, arc_w);
    lv_arc_set_bg_angles(arc, 270, 630);
    uint8_t hours = recovery_time->max_value / 3600;
    if(0 != (recovery_time->max_value % 3600)) {
        hours++;
    }
    lv_arc_set_range(arc, 0, hours);
    hours = recovery_time->value / 3600;
    if(0 != (recovery_time->value % 3600)) {
        hours++;
    }
    lv_arc_set_value(arc, hours);
    lv_obj_align(arc, bg, LV_ALIGN_CENTER, 0, 0);
    lv_obj_set_click(arc, false);

    lv_obj_t * label = lv_label_create(bg, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
    lv_label_set_long_mode(label, LV_LABEL_LONG_SROLL_CIRC);
    lv_obj_set_width(label, obj_w - 2 * arc_w);
    lv_label_set_text_id(label, WATCH_TEXT_ID_RECOVERY_TIME);
    lv_obj_align(label, bg, LV_ALIGN_IN_TOP_MID, 0, arc_w + obj_h / 10);

    label = lv_label_create(bg, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_BIG);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    char str[4] = {};
    snprintf(str, 4, "%d", hours);
    lv_label_set_text(label, str);
    lv_obj_align(label, bg, LV_ALIGN_CENTER, 0, 0);

    lv_obj_t * label1 = lv_label_create(bg, NULL);
    lv_obj_set_style_local_text_font(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_SMALL);
    lv_obj_set_style_local_text_letter_space(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    if(1 < hours) {
        lv_label_set_text_id(label1, WATCH_TEXT_ID_ACT_HOURS);
    } else {
        lv_label_set_text_id(label1, WATCH_TEXT_ID_ACT_HOUR);
    }
    lv_obj_align(label1, label, LV_ALIGN_OUT_BOTTOM_MID, 0, lv_obj_get_height(label1));
}

static void exercise_record_add_training_effect(lv_obj_t * page, lv_coord_t arc_w,
                                                exercise_record_training_effect_t * training_effect)
{
    lv_coord_t obj_w = lv_obj_get_width(page);
    lv_coord_t obj_h = lv_obj_get_height(page);
    int32_t start_angle_1 = 180 - EXERCISE_RECORD_TRANING_EFFECT_ARC_ANGLE / 2;
    int32_t end_angle_1 = start_angle_1 + EXERCISE_RECORD_TRANING_EFFECT_ARC_ANGLE;
    int32_t start_angle_2 = 360 - EXERCISE_RECORD_TRANING_EFFECT_ARC_ANGLE / 2;
    int32_t end_angle_2 = start_angle_2 + EXERCISE_RECORD_TRANING_EFFECT_ARC_ANGLE;
    lv_coord_t small_font_h = lv_font_get_line_height(LV_THEME_WATCH_NIGHT_FONT_SMALL);
    lv_coord_t big_font_h = lv_font_get_line_height(LV_THEME_WATCH_NIGHT_FONT_BIG);
    lv_color_t color[2] = {LV_COLOR_YELLOW, LV_COLOR_MAKE(0x00, 0xFF, 0x40)};
    uint8_t label_value[2] = {training_effect->aerobic_training_value,
                              training_effect->anaerobic_training_value};
    uint16_t txt_id[2] = {WATCH_TEXT_ID_AEROBIC_TRAINING, WATCH_TEXT_ID_ANAEROBIC_TRAINING};

    lv_obj_t * bg = lv_cont_create(page, NULL);
    lv_obj_set_style_local_bg_opa(bg, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0);
    lv_obj_set_size(bg, obj_w, obj_h);
    lv_obj_set_click(bg, false);

    if(LV_BIDI_DIR_RTL == lv_obj_get_base_dir(bg)) {
        lv_color_t temp_color = color[0];
        color[0] = color[1];
        color[1] = temp_color;

        uint8_t temp_label_value = label_value[0];
        label_value[0] = label_value[1];
        label_value[1] = temp_label_value;

        uint16_t temp_txt_id = txt_id[0];
        txt_id[0] = txt_id[1];
        txt_id[1] = temp_txt_id;
    }

    lv_obj_t * arc[2];
    arc[0] = lv_arc_create(bg, NULL);
    lv_obj_set_size(arc[0], obj_w, obj_h);
    lv_obj_set_style_local_bg_opa(arc[0], LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_OPA_0);
    lv_obj_set_style_local_line_rounded(arc[0], LV_ARC_PART_BG, LV_STATE_DEFAULT, false);
    lv_obj_set_style_local_line_color(arc[0], LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_obj_set_style_local_line_opa(arc[0], LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_OPA_50);
    lv_obj_set_style_local_line_width(arc[0], LV_ARC_PART_BG, LV_STATE_DEFAULT, arc_w);
    lv_obj_set_style_local_line_color(arc[0], LV_ARC_PART_INDIC, LV_STATE_DEFAULT, color[0]);
    lv_obj_set_style_local_line_opa(arc[0], LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_OPA_90);
    lv_obj_set_style_local_line_rounded(arc[0], LV_ARC_PART_INDIC, LV_STATE_DEFAULT, false);
    lv_obj_set_style_local_line_width(arc[0], LV_ARC_PART_INDIC, LV_STATE_DEFAULT, arc_w);
    lv_arc_set_bg_angles(arc[0], start_angle_1, end_angle_1);
    lv_obj_align(arc[0], bg, LV_ALIGN_CENTER, 0, 0);
    lv_obj_set_click(arc[0], false);

    arc[1] = lv_arc_create(bg, arc[0]);
    lv_obj_set_style_local_line_color(arc[1], LV_ARC_PART_INDIC, LV_STATE_DEFAULT, color[1]);
    lv_obj_set_style_local_line_opa(arc[1], LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_OPA_COVER);
    lv_obj_set_size(arc[1], LV_HOR_RES, LV_VER_RES);
    lv_arc_set_bg_angles(arc[1], start_angle_2, end_angle_2);
    lv_obj_align(arc[1], bg, LV_ALIGN_CENTER, 0, 0);
    lv_obj_set_click(arc[1], false);

    if(10 <= label_value[0]) {
        int32_t angle = start_angle_1 + EXERCISE_RECORD_TRANING_EFFECT_ARC_ANGLE * (
            label_value[0] - 9) / 41;
        lv_arc_set_angles(arc[0], start_angle_1, angle);
    }

    if(10 <= label_value[1]) {
        int32_t angle = end_angle_2 - EXERCISE_RECORD_TRANING_EFFECT_ARC_ANGLE * (
            label_value[1] - 9) / 41;
        if(0 > angle) {
            angle += 360;
        } else if(360 <= angle) {
            angle -= 360;
            end_angle_2 -= 360;
        }
        lv_arc_set_angles(arc[1], angle, end_angle_2);
    }

    lv_obj_t * label = lv_label_create(bg, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
    lv_label_set_long_mode(label, LV_LABEL_LONG_SROLL_CIRC);
    lv_obj_set_width(label, obj_w * 2 / 3);
    lv_label_set_text_id(label, WATCH_TEXT_ID_TRAINING_EFFECT);
    lv_obj_align(label, bg, LV_ALIGN_IN_TOP_MID, 0, arc_w + obj_h / 10);

    label = lv_label_create(bg, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_SMALL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    if(10 > label_value[0]) {
        lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                          LV_COLOR_GRAY);
    }
    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
    lv_label_set_long_mode(label, LV_LABEL_LONG_SROLL_CIRC);
    lv_obj_set_width(label, obj_w / 2 - 2 * arc_w);
    lv_label_set_text_id(label, txt_id[0]);
    lv_coord_t pos_y = (big_font_h - small_font_h) / 2;
    lv_obj_align(label, bg, LV_ALIGN_IN_LEFT_MID, arc_w * 3 / 2, pos_y);

    lv_obj_t * label1 = lv_label_create(bg, NULL);
    lv_obj_set_style_local_text_font(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_BIG);
    lv_obj_set_style_local_text_letter_space(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    if(10 > label_value[0]) {
        lv_obj_set_style_local_text_color(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                          LV_COLOR_GRAY);
        lv_label_set_text(label1, "1.0");

        lv_obj_t * label2 = lv_label_create(bg, NULL);
        lv_obj_set_style_local_text_font(label2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                         LV_THEME_WATCH_NIGHT_FONT_SMALL);
        lv_obj_set_style_local_text_letter_space(label2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
        lv_label_set_text(label2, "<");

        lv_obj_align(label1, label, LV_ALIGN_OUT_TOP_MID, lv_obj_get_width(label2) / 2, 0);
        lv_obj_align(label2, label1, LV_ALIGN_OUT_LEFT_MID, 0, 0);
    } else {
        lv_obj_set_style_local_text_color(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color[0]);
        char str[4] = "0.0";
        str[0] = label_value[0] / 10 + '0';
        str[2] = label_value[0] % 10 + '0';
        lv_label_set_text(label1, str);
        lv_obj_align(label1, label, LV_ALIGN_OUT_TOP_MID, 0, 0);
    }

    label = lv_label_create(bg, label);
    if(10 > label_value[1]) {
        lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                          LV_COLOR_GRAY);
    }
    lv_label_set_text_id(label, txt_id[1]);
    lv_obj_align(label, bg, LV_ALIGN_IN_RIGHT_MID, -arc_w * 3 / 2, pos_y);

    label1 = lv_label_create(bg, label1);
    if(10 > label_value[1]) {
        lv_obj_set_style_local_text_color(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                          LV_COLOR_GRAY);
        lv_label_set_text(label1, "1.0");

        lv_obj_t * label2 = lv_label_create(bg, NULL);
        lv_obj_set_style_local_text_font(label2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                         LV_THEME_WATCH_NIGHT_FONT_SMALL);
        lv_obj_set_style_local_text_letter_space(label2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
        lv_label_set_text(label2, "<");

        lv_obj_align(label1, label, LV_ALIGN_OUT_TOP_MID, lv_obj_get_width(label2) / 2, 0);
        lv_obj_align(label2, label1, LV_ALIGN_OUT_LEFT_MID, 0, 0);
    } else {
        lv_obj_set_style_local_text_color(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color[1]);
        char str[4] = "0.0";
        str[0] = label_value[1] / 10 + '0';
        str[2] = label_value[1] % 10 + '0';
        lv_label_set_text(label1, str);
        lv_obj_align(label1, label, LV_ALIGN_OUT_TOP_MID, 0, 0);
    }
}

static void exercise_record_add_hr_zone(lv_obj_t * page, exercise_record_hr_zone_t * hr_zone)
{
    uint32_t total_time = 0;
    for(uint8_t i = 0;i < EXERCISE_RECORD_HR_ZONE_NUM;i++) {
        total_time += hr_zone->time[i];
    }
    lv_coord_t obj_w = lv_obj_get_width(page);
    lv_coord_t obj_h = lv_obj_get_height(page);
    lv_coord_t label_bar_inner = 2;
    lv_coord_t small_font_h = lv_font_get_line_height(LV_THEME_WATCH_NIGHT_FONT_SMALL);
    lv_coord_t normal_font_h = lv_font_get_line_height(LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_coord_t bar_h = small_font_h / 3;
    lv_coord_t inner = (obj_h - 4 * normal_font_h - EXERCISE_RECORD_HR_ZONE_NUM * small_font_h -
                        label_bar_inner * EXERCISE_RECORD_HR_ZONE_NUM -
                        bar_h * EXERCISE_RECORD_HR_ZONE_NUM) / EXERCISE_RECORD_HR_ZONE_NUM;
    if(0 >= inner) {
        inner = 0;
    }
    lv_coord_t bar_w = obj_w * 2 / 3;
    lv_coord_t pos_x = (obj_w - bar_w) / 2;
    char str[20];

    lv_obj_t * cont = lv_cont_create(page, NULL);
    lv_obj_set_style_local_bg_opa(cont, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0);
    lv_obj_set_size(cont, obj_w, obj_h);
    lv_obj_set_click(cont, false);

    lv_bidi_dir_t dir = lv_obj_get_base_dir(cont);

    lv_obj_t * label = lv_label_create(cont, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
    lv_label_set_long_mode(label, LV_LABEL_LONG_SROLL_CIRC);
    lv_obj_set_width(label, obj_w);
    lv_label_set_text_id(label, WATCH_TEXT_ID_HEART_RATE_ZONE);
    lv_obj_align(label, cont, LV_ALIGN_IN_TOP_MID, 0, obj_h / 10);

    uint16_t txt_id[EXERCISE_RECORD_HR_ZONE_NUM] = {WATCH_TEXT_ID_WARM_UP,
                                                 WATCH_TEXT_ID_FAT_BURNING,
                                                 WATCH_TEXT_ID_AEROBIC_TRAINING,
                                                 WATCH_TEXT_ID_ANAEROBIC_TRAINING,
                                                 WATCH_TEXT_ID_EXTREME};
    lv_color_t color[EXERCISE_RECORD_HR_ZONE_NUM] = {LV_COLOR_BLUE, LV_COLOR_GREEN, LV_COLOR_YELLOW,
                                                  LV_COLOR_ORANGE, LV_COLOR_RED};
    int8_t i = EXERCISE_RECORD_HR_ZONE_NUM - 1;
    label = lv_label_create(cont, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_SMALL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_label_set_text_id(label, txt_id[i]);

    lv_obj_t * label1 = lv_label_create(cont, label);
    if(1 < hr_zone->time[i]) {
        snprintf(str, 20, "%d%s", hr_zone->time[i],
                 (char *)lv_lang_get_text(WATCH_TEXT_ID_ACT_MINS));
    } else {
        snprintf(str, 20, "%d%s", hr_zone->time[i],
                 (char *)lv_lang_get_text(WATCH_TEXT_ID_ACT_MIN));
    }
    lv_label_set_text(label1, str);
    lv_label_set_long_mode(label, LV_LABEL_LONG_SROLL_CIRC);
    lv_obj_set_width(label, bar_w - lv_obj_get_width(label1) - 4);

    if(LV_BIDI_DIR_RTL == dir) {
        lv_obj_align(label, cont, LV_ALIGN_IN_TOP_RIGHT, -pos_x, 2 * normal_font_h + inner);
        lv_obj_align(label1, cont, LV_ALIGN_IN_TOP_LEFT, pos_x, 2 * normal_font_h + inner);
    } else {
        lv_obj_align(label, cont, LV_ALIGN_IN_TOP_LEFT, pos_x, 2 * normal_font_h + inner);
        lv_obj_align(label1, cont, LV_ALIGN_IN_TOP_RIGHT, -pos_x, 2 * normal_font_h + inner);
    }

    lv_obj_t * bar = lv_bar_create(cont, NULL);
    lv_obj_set_style_local_bg_color(bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, color[i]);
    lv_obj_set_style_local_bg_opa(bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_OPA_30);
    lv_obj_set_style_local_border_width(bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_bg_color(bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, color[i]);
    lv_obj_set_style_local_bg_opa(bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_OPA_COVER);
    lv_obj_set_size(bar, bar_w, bar_h);
    uint16_t min_time = total_time * bar_h / bar_w;
    if(min_time >= hr_zone->time[i]) {
        lv_bar_set_range(bar, 0, bar_w);
        lv_bar_set_value(bar, bar_h, LV_ANIM_OFF);
    } else {
        lv_bar_set_range(bar, 0, total_time);
        lv_bar_set_value(bar, hr_zone->time[i], LV_ANIM_OFF);
    }
    lv_obj_set_click(bar, false);
    lv_obj_align(bar, cont, LV_ALIGN_IN_TOP_MID, 0,
                 lv_obj_get_y(label) + small_font_h + label_bar_inner);
    i--;

    for(;i >= 0;i--) {
        label = lv_label_create(cont, label);
        lv_label_set_text_id(label, txt_id[i]);

        label1 = lv_label_create(cont, label1);
        if(1 < hr_zone->time[i]) {
            snprintf(str, 20, "%d%s", hr_zone->time[i],
                     (char *)lv_lang_get_text(WATCH_TEXT_ID_ACT_MINS));
        } else {
            snprintf(str, 20, "%d%s", hr_zone->time[i],
                     (char *)lv_lang_get_text(WATCH_TEXT_ID_ACT_MIN));
        }
        lv_label_set_text(label1, str);
        lv_obj_set_width(label, bar_w - lv_obj_get_width(label1) - 4);

        if(LV_BIDI_DIR_RTL == dir) {
            lv_obj_align(label, bar, LV_ALIGN_OUT_BOTTOM_RIGHT, 0, inner);
            lv_obj_align(label1, bar, LV_ALIGN_OUT_BOTTOM_LEFT, 0, inner);
        } else {
            lv_obj_align(label, bar, LV_ALIGN_OUT_BOTTOM_LEFT, 0, inner);
            lv_obj_align(label1, bar, LV_ALIGN_OUT_BOTTOM_RIGHT, 0, inner);
        }

        bar = lv_bar_create(cont, bar);
        lv_obj_set_style_local_bg_color(bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, color[i]);
        lv_obj_set_style_local_bg_color(bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, color[i]);
        lv_bar_set_range(bar, 0, total_time);
        if(min_time >= hr_zone->time[i]) {
            lv_bar_set_range(bar, 0, bar_w);
            lv_bar_set_value(bar, bar_h, LV_ANIM_OFF);
        } else {
            lv_bar_set_range(bar, 0, total_time);
            lv_bar_set_value(bar, hr_zone->time[i], LV_ANIM_OFF);
        }
        lv_obj_align(bar, cont, LV_ALIGN_IN_TOP_MID, 0,
                     lv_obj_get_y(label) + small_font_h + label_bar_inner);
    }
}

static void exercise_record_get_str(uint32_t value, uint8_t precision, char * str, uint8_t str_len,
                                    bool is_pace)
{
    if(is_pace) {
        snprintf(str, str_len, "%d'%02d\"", value / 60, value % 60);
        return;
    }

    uint32_t scale = 1;
    for(uint8_t i = 0;i < precision;i++) {
        scale *= 10;
    }
    snprintf(str, str_len, "%d", value / scale);
    if(0 < precision) {
        uint16_t dec = value % scale;
        if(0 < dec) {
            uint8_t len = strlen(str);
            str[len++] = '.';
            for(uint8_t i = 0;i < precision;i++) {
                scale /= 10;
                if(scale > dec) {
                    str[len++] = '0';
                } else {
                    str[len++] = dec / scale + '0';
                    dec %= scale;
                    if(0 == dec) break;
                }
            }
            str[len] = '\0';
        }
    }
}

static void exercise_record_add_curve(lv_obj_t * page, uint32_t sport_time, uint16_t title_txt_id,
                                      uint32_t min_value, uint32_t max_value, uint32_t aver_value,
                                      lv_color_t color, lv_coord_t * y_array, uint16_t point_count,
                                      uint16_t unit_txt_id, uint8_t precision, bool is_pace)
{
    lv_coord_t obj_w = lv_obj_get_width(page);
    lv_coord_t obj_h = lv_obj_get_height(page);
    lv_coord_t small_font_h = lv_font_get_line_height(LV_THEME_WATCH_NIGHT_FONT_SMALL);
    lv_coord_t normal_font_h = lv_font_get_line_height(LV_THEME_WATCH_NIGHT_FONT_NORMAL);

    lv_obj_t * bg = lv_cont_create(page, NULL);
    lv_obj_set_style_local_bg_opa(bg, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0);
    lv_obj_set_size(bg, obj_w, obj_h);
    lv_obj_set_click(bg, false);

    lv_bidi_dir_t dir = lv_obj_get_base_dir(bg);

    lv_obj_t * label = lv_label_create(bg, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
    lv_label_set_long_mode(label, LV_LABEL_LONG_SROLL_CIRC);
    lv_obj_set_width(label, obj_w * 2 / 3);
    lv_label_set_text_id(label, title_txt_id);
    lv_obj_align(label, bg, LV_ALIGN_IN_TOP_MID, 0, obj_h / 10);

    lv_obj_t * chart = lv_chart_create(bg, NULL);
    lv_obj_set_style_local_bg_opa(chart, LV_CHART_PART_BG, LV_STATE_DEFAULT, LV_OPA_0);
    lv_obj_set_style_local_pad_top(chart, LV_CHART_PART_BG, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_pad_bottom(chart, LV_CHART_PART_BG, LV_STATE_DEFAULT, 2);
    lv_obj_set_style_local_pad_left(chart, LV_CHART_PART_BG, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_pad_right(chart, LV_CHART_PART_BG, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_border_opa(chart, LV_CHART_PART_BG, LV_STATE_DEFAULT, LV_OPA_COVER);
    lv_obj_set_style_local_border_color(chart, LV_CHART_PART_BG, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_obj_set_style_local_border_width(chart, LV_CHART_PART_BG, LV_STATE_DEFAULT, 2);
    lv_obj_set_style_local_border_side(chart, LV_CHART_PART_BG, LV_STATE_DEFAULT,
                                       LV_BORDER_SIDE_BOTTOM);
    lv_obj_set_style_local_line_opa(chart, LV_CHART_PART_SERIES_BG, LV_STATE_DEFAULT, LV_OPA_0);
    lv_obj_set_style_local_size(chart, LV_CHART_PART_SERIES, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_line_opa(chart, LV_CHART_PART_SERIES, LV_STATE_DEFAULT, LV_OPA_COVER);
    lv_obj_set_style_local_bg_opa(chart, LV_CHART_PART_SERIES, LV_STATE_DEFAULT, LV_OPA_COVER);
    lv_obj_set_style_local_bg_main_stop(chart, LV_CHART_PART_SERIES, LV_STATE_DEFAULT, 220);
    lv_obj_set_style_local_bg_grad_stop(chart, LV_CHART_PART_SERIES, LV_STATE_DEFAULT, 60);
    lv_obj_set_style_local_bg_grad_dir(chart, LV_CHART_PART_SERIES, LV_STATE_DEFAULT,
                                       LV_GRAD_DIR_VER);
    lv_obj_set_size(chart, obj_w * 4 / 5, obj_h * 2 / 7);
    lv_chart_set_point_count(chart, point_count);
    lv_chart_set_y_range(chart, LV_CHART_AXIS_PRIMARY_Y,
                         min_value - (max_value - min_value) / 10, max_value);
    lv_chart_series_t * ser = lv_chart_add_series(chart, color);
    lv_chart_set_points(chart, ser, y_array);
    lv_obj_set_click(chart, false);

    label = lv_label_create(bg, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_SMALL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_label_set_text(label, "00:00:00");

    lv_obj_align(chart, bg, LV_ALIGN_CENTER, 0, -small_font_h);

    lv_coord_t pos_x = 2;
    if(LV_BIDI_DIR_RTL == dir) {
        lv_obj_set_base_dir(label, LV_BIDI_DIR_LTR);
        lv_obj_align(label, chart, LV_ALIGN_OUT_BOTTOM_RIGHT, -pos_x, 0);
    } else {
        lv_obj_align(label, chart, LV_ALIGN_OUT_BOTTOM_LEFT, pos_x, 0);
    }

    label = lv_label_create(bg, label);
    char str[10];
    snprintf(str, 10, "%02d:%02d:%02d", sport_time / 3600, (sport_time / 60) % 60, sport_time % 60);
    lv_label_set_text(label, str);
    if(LV_BIDI_DIR_RTL == dir) {
        lv_obj_set_base_dir(label, LV_BIDI_DIR_LTR);
        lv_obj_align(label, chart, LV_ALIGN_OUT_BOTTOM_LEFT, pos_x, 0);
    } else {
        lv_obj_align(label, chart, LV_ALIGN_OUT_BOTTOM_RIGHT, -pos_x, 0);
    }

    lv_coord_t pos_y = lv_obj_get_y(label) + obj_h / 15;
    lv_coord_t inner = (obj_h * 7 / 8 - pos_y - 2 * normal_font_h) / 2;
    lv_coord_t hor_offset = obj_w / 20;
    label = lv_label_create(bg, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    if(is_pace) {
        if(LV_BIDI_DIR_RTL == dir) {
            lv_obj_set_base_dir(label, LV_BIDI_DIR_LTR);
        }
        exercise_record_get_str(min_value, precision, str, 10, true);
    } else {
        exercise_record_get_str(max_value, precision, str, 10, false);
    }
    lv_label_set_text(label, str);

    lv_obj_t * img = lv_img_create(bg, NULL);
    lv_img_set_src(img, ICON_HEART_RATE_HIGHEST);
    lv_coord_t img_h = lv_obj_get_height(img);
    lv_coord_t img_real_h = normal_font_h * 2 / 5;
    lv_img_set_zoom(img, LV_IMG_ZOOM_NONE * img_real_h / img_h);
    lv_coord_t img_w = lv_obj_get_width(img);
    lv_coord_t pad_hor = 2;
    if(is_pace) {
        lv_obj_align(label, bg, LV_ALIGN_IN_TOP_LEFT,
                     (obj_w - lv_obj_get_width(label) - pad_hor + img_real_h * img_w / img_h) / 2,
                     pos_y + inner);
    } else {
        lv_obj_align(label, bg, LV_ALIGN_IN_TOP_MID, -hor_offset - lv_obj_get_width(label) / 2,
                     pos_y + inner);
    }
    lv_coord_t img_real_w = img_real_h * img_w / img_h;
    lv_obj_align(img, label, LV_ALIGN_OUT_LEFT_MID,
                 (img_w - img_real_w) / 2 - pad_hor, 2);

    if(LV_BIDI_DIR_RTL == dir) {
        lv_obj_set_x(label, lv_obj_get_x(img) - (img_w - img_real_h * img_w / img_h) / 2);
        lv_obj_align(img, label, LV_ALIGN_OUT_RIGHT_MID, -(img_w - img_real_w) / 2 + pad_hor, 2);
    }

    if(false == is_pace) {
        img = lv_img_create(bg, NULL);
        lv_img_set_src(img, ICON_HEART_RATE_LOWEST);
        img_h = lv_obj_get_height(img);
        img_w = lv_obj_get_width(img);
        lv_img_set_zoom(img, LV_IMG_ZOOM_NONE * img_real_h / img_h);

        exercise_record_get_str(min_value, precision, str, 10, is_pace);
        label = lv_label_create(bg, label);
        lv_label_set_text(label, str);
        lv_obj_align(label, bg, LV_ALIGN_IN_TOP_MID,
                     hor_offset + lv_obj_get_width(label) / 2 + img_real_w + pad_hor, pos_y + inner);
        lv_obj_align(img, label, LV_ALIGN_OUT_LEFT_MID,
                     (img_w - img_real_w) / 2 - pad_hor, 2);

        if(LV_BIDI_DIR_RTL == dir) {
            lv_obj_set_x(label, lv_obj_get_x(img) - (img_w - img_real_h * img_w / img_h) / 2);
            lv_obj_align(img, label, LV_ALIGN_OUT_RIGHT_MID, -(img_w - img_real_w) / 2 + pad_hor, 2);
        }
    }

    pos_y = lv_obj_get_y(label) + normal_font_h + inner;

    label = lv_label_create(bg, label);
    if(is_pace && (LV_BIDI_DIR_RTL == dir)) {
        lv_obj_set_base_dir(label, LV_BIDI_DIR_LTR);
    }
    exercise_record_get_str(aver_value, precision, str, 10, is_pace);
    lv_label_set_text(label, str);

    lv_obj_t * label_l = lv_label_create(bg, NULL);
    lv_obj_set_style_local_text_font(label_l, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_SMALL);
    lv_obj_set_style_local_text_letter_space(label_l, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_text_color(label_l, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_label_set_text_id(label_l, WATCH_TEXT_ID_AVERAGE);

    lv_obj_t * label_r = lv_label_create(bg, label_l);
    lv_label_set_text_id(label_r, unit_txt_id);

    lv_coord_t label_w = lv_obj_get_width(label);
    lv_coord_t label_l_w = lv_obj_get_width(label_l);
    lv_coord_t label_l_r = lv_obj_get_width(label_r);
    if(LV_BIDI_DIR_RTL == dir) {
        lv_obj_align(label, bg, LV_ALIGN_IN_TOP_RIGHT,
                     -(obj_w + label_l_w - label_w - label_l_r) / 2, pos_y);
        lv_obj_align(label_l, label, LV_ALIGN_OUT_RIGHT_MID, pad_hor, 0);
        lv_obj_align(label_r, label, LV_ALIGN_OUT_LEFT_MID, -pad_hor, 0);
    } else {
        lv_obj_align(label, bg, LV_ALIGN_IN_TOP_LEFT,
                     (obj_w + label_l_w - label_w - label_l_r) / 2, pos_y);
        lv_obj_align(label_l, label, LV_ALIGN_OUT_LEFT_MID, -pad_hor, 0);
        lv_obj_align(label_r, label, LV_ALIGN_OUT_RIGHT_MID, pad_hor, 0);
    }
}

static void exercise_record_add_content(lv_obj_t * page, exercise_record_content_t * content)
{
    lv_coord_t obj_w = lv_obj_get_width(page);
    lv_coord_t obj_h = lv_obj_get_height(page);
    lv_coord_t small_font_h = lv_font_get_line_height(LV_THEME_WATCH_NIGHT_FONT_SMALL);
    lv_coord_t normal_font_h = lv_font_get_line_height(LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_coord_t inner = (obj_h - 5 * normal_font_h - 5 * small_font_h) / 3;

    lv_obj_t * cont = lv_cont_create(page, NULL);
    lv_obj_set_style_local_bg_opa(cont, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0);
    lv_obj_set_size(cont, obj_w, obj_h);
    lv_obj_set_click(cont, false);

    lv_bidi_dir_t dir = lv_obj_get_base_dir(cont);

    lv_obj_t * label = lv_label_create(cont, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
    lv_label_set_long_mode(label, LV_LABEL_LONG_SROLL_CIRC);
    lv_obj_set_width(label, obj_w);
    lv_label_set_text_id(label, content->title_txt_id);
    lv_obj_align(label, cont, LV_ALIGN_IN_TOP_MID, 0, obj_w / 10);

    lv_obj_t * label1 = lv_label_create(cont, NULL);
    lv_obj_set_style_local_text_font(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_SMALL);
    lv_obj_set_style_local_text_letter_space(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_text_color(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_label_set_long_mode(label1, LV_LABEL_LONG_SROLL_CIRC);
    lv_obj_set_width(label1, obj_w * 3 / 5);
    lv_label_set_text_id(label1, content->info[0].txt_id);

    label = lv_label_create(cont, label);
    lv_obj_set_width(label, obj_w * 3 / 5);
    lv_label_set_text(label, content->info[0].str);

    lv_obj_t * img = lv_img_create(cont, NULL);
    lv_img_set_src(img, content->info[0].img_src);
    lv_coord_t img_h = lv_obj_get_height(img);
    lv_coord_t img_w = lv_obj_get_width(img);
    lv_coord_t zoom_w = small_font_h + normal_font_h;
    lv_img_set_zoom(img, LV_IMG_ZOOM_NONE * zoom_w / img_h);

    if(LV_BIDI_DIR_RTL == dir) {
        lv_label_set_align(label, LV_LABEL_ALIGN_RIGHT);
        lv_obj_align(label1, cont, LV_ALIGN_IN_TOP_RIGHT, -obj_w * 7 / 20, 2 * normal_font_h + inner);
        lv_obj_align(label, label1, LV_ALIGN_OUT_BOTTOM_RIGHT, 0, 0);
        lv_obj_align(img, cont, LV_ALIGN_IN_TOP_RIGHT,
                     -obj_w + lv_obj_get_x(label1) + lv_obj_get_width(label1) +
                     (zoom_w + img_w) / 2 + small_font_h / 2,
                     (lv_obj_get_y(label1) + lv_obj_get_y(label) +
                      lv_obj_get_height(label) - img_h) / 2);
    } else {
        lv_label_set_align(label, LV_LABEL_ALIGN_LEFT);
        lv_obj_align(label1, cont, LV_ALIGN_IN_TOP_LEFT, obj_w * 7 / 20, 2 * normal_font_h + inner);
        lv_obj_align(label, label1, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
        lv_obj_align(img, cont, LV_ALIGN_IN_TOP_LEFT,
                     lv_obj_get_x(label1) - (zoom_w + img_w) / 2 - small_font_h / 2,
                     (lv_obj_get_y(label1) + lv_obj_get_y(label) +
                      lv_obj_get_height(label) - img_h) / 2);
    }

    if(WATCH_TEXT_ID_NONE == content->info[1].txt_id) return;

    label1 = lv_label_create(cont, label1);
    lv_label_set_text_id(label1, content->info[1].txt_id);

    label = lv_label_create(cont, label);
    lv_label_set_text(label, content->info[1].str);

    img = lv_img_create(cont, NULL);
    lv_img_set_src(img, content->info[1].img_src);
    img_h = lv_obj_get_height(img);
    img_w = lv_obj_get_width(img);
    lv_img_set_zoom(img, LV_IMG_ZOOM_NONE * zoom_w / img_h);

    if(LV_BIDI_DIR_RTL == dir) {
        lv_obj_align(label1, label, LV_ALIGN_OUT_BOTTOM_RIGHT, 0, inner);
        lv_obj_align(label, label1, LV_ALIGN_OUT_BOTTOM_RIGHT, 0, 0);
        lv_obj_align(img, cont, LV_ALIGN_IN_TOP_RIGHT,
                     -obj_w + lv_obj_get_x(label1) + lv_obj_get_width(label1) +
                     (zoom_w + img_w) / 2 + small_font_h / 2,
                     (lv_obj_get_y(label1) + lv_obj_get_y(label) +
                      lv_obj_get_height(label) - img_h) / 2);
    } else {
        lv_obj_align(label1, label, LV_ALIGN_OUT_BOTTOM_LEFT, 0, inner);
        lv_obj_align(label, label1, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
        lv_obj_align(img, cont, LV_ALIGN_IN_TOP_LEFT,
                     lv_obj_get_x(label1) - (zoom_w + img_w) / 2 - small_font_h / 2,
                     (lv_obj_get_y(label1) + lv_obj_get_y(label) +
                      lv_obj_get_height(label) - img_h) / 2);
    }

    if(WATCH_TEXT_ID_NONE == content->info[2].txt_id) return;

    label1 = lv_label_create(cont, label1);
    lv_label_set_text_id(label1, content->info[2].txt_id);

    label = lv_label_create(cont, label);
    lv_label_set_text(label, content->info[2].str);

    img = lv_img_create(cont, NULL);
    lv_img_set_src(img, content->info[2].img_src);
    img_h = lv_obj_get_height(img);
    img_w = lv_obj_get_width(img);
    lv_img_set_zoom(img, LV_IMG_ZOOM_NONE * zoom_w / img_h);

    if(LV_BIDI_DIR_RTL == dir) {
        lv_obj_align(label1, label, LV_ALIGN_OUT_BOTTOM_RIGHT, 0, inner);
        lv_obj_align(label, label1, LV_ALIGN_OUT_BOTTOM_RIGHT, 0, 0);
        lv_obj_align(img, cont, LV_ALIGN_IN_TOP_RIGHT,
                     -obj_w + lv_obj_get_x(label1) + lv_obj_get_width(label1) +
                     (zoom_w + img_w) / 2 + small_font_h / 2,
                     (lv_obj_get_y(label1) + lv_obj_get_y(label) +
                      lv_obj_get_height(label) - img_h) / 2);
    } else {
        lv_obj_align(label1, label, LV_ALIGN_OUT_BOTTOM_LEFT, 0, inner);
        lv_obj_align(label, label1, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
        lv_obj_align(img, cont, LV_ALIGN_IN_TOP_LEFT,
                     lv_obj_get_x(label1) - (zoom_w + img_w) / 2 - small_font_h / 2,
                     (lv_obj_get_y(label1) + lv_obj_get_y(label) +
                      lv_obj_get_height(label) - img_h) / 2);
    }
}

static void exercise_record_get_sport_info(uint8_t type, uint16_t * txt_id, void ** img_src,
                                           lv_color_t * img_bg_color)
{
#if USE_LV_WATCH_EXERCISE != 0
    exercise_sport_info_t sport_info = {};
    exercise_get_sport_info(type, &sport_info);
    if(txt_id) *txt_id = sport_info.txt_id;
    if(img_src) *img_src = sport_info.img_src;
    if(img_bg_color) *img_bg_color = sport_info.img_bg_color;
#else
    switch(type) {
    case 1:
        if(txt_id) *txt_id = WATCH_TEXT_ID_OUTDOOR_RUNNING;
        if(img_src) *img_src = ICON_EXERCISE_RUNNING;
        if(img_bg_color) *img_bg_color = LV_COLOR_MAKE(248, 99, 46);
        break;
    case 2:
        if(txt_id) *txt_id = WATCH_TEXT_ID_OUTDOOR_CYCLING;
        if(img_src) *img_src = ICON_EXERCISE_CYCLING;
        if(img_bg_color) *img_bg_color = LV_COLOR_MAKE(248, 53, 92);
        break;
    case 3:
        if(txt_id) *txt_id = WATCH_TEXT_ID_WALKING;
        if(img_src) *img_src = ICON_EXERCISE_WALKING;
        if(img_bg_color) *img_bg_color = LV_COLOR_MAKE(249, 100, 47);
        break;
    case 4:
        if(txt_id) *txt_id = WATCH_TEXT_ID_FREE_TRAINING;
        if(img_src) *img_src = ICON_EXERCISE_FREE_TRAINING;
        if(img_bg_color) *img_bg_color = LV_COLOR_MAKE(72, 200, 78);
        break;
    default:
        printf("%s: warning, invalid type %d\n", __FUNCTION__, type);
        if(txt_id) *txt_id = WATCH_TEXT_ID_OUTDOOR_RUNNING;
        if(img_src) *img_src = ICON_EXERCISE_RUNNING;
        break;
    }
#endif
}

static void exercise_record_add_homepage(lv_obj_t * page, exercise_record_base_t * nv)
{
    lv_coord_t obj_w = lv_obj_get_width(page);
    lv_coord_t obj_h = lv_obj_get_height(page);

    uint16_t txt_id;
    void * img_src;
    lv_color_t img_bg_color;
    exercise_record_get_sport_info(nv->sport_type, &txt_id, &img_src, &img_bg_color);

    lv_obj_t * cont = lv_cont_create(page, NULL);
    lv_obj_set_style_local_bg_opa(cont, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0);
    lv_obj_set_size(cont, obj_w, obj_h);
    lv_obj_set_click(cont, false);

    lv_obj_t * img_bg = lv_cont_create(cont, NULL);
    lv_obj_set_click(img_bg, false);
    lv_obj_set_style_local_bg_opa(img_bg, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_COVER);
    lv_obj_set_style_local_bg_color(img_bg, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, img_bg_color);
    lv_obj_set_style_local_radius(img_bg, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
    lv_coord_t img_bg_size = obj_h * 2 / 5;
    lv_obj_set_size(img_bg, img_bg_size, img_bg_size);
    lv_obj_align(img_bg, cont, LV_ALIGN_IN_TOP_MID, 0, obj_h / 5);

    lv_obj_t * img = lv_img_create(img_bg, NULL);
    lv_img_set_src(img, img_src);
    lv_img_set_zoom(img, LV_IMG_ZOOM_NONE * img_bg_size / lv_obj_get_height(img));
    lv_obj_align(img, img_bg, LV_ALIGN_CENTER, 0, 0);

    lv_obj_t * label = lv_label_create(cont, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_BIG);
    lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_label_set_text(label, LV_SYMBOL_UP);
    lv_obj_align(label, cont, LV_ALIGN_IN_BOTTOM_MID, 0, -obj_h / 3 / 15);
    lv_coord_t bottom_y = lv_obj_get_y(label);

    label = lv_label_create(cont, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
    lv_label_set_long_mode(label, LV_LABEL_LONG_SROLL_CIRC);
    lv_obj_set_width(label, obj_w * 3 / 4);
    lv_label_set_text_id(label, txt_id);

    lv_obj_t * label1 = lv_label_create(cont, NULL);
    lv_obj_set_style_local_text_font(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_SMALL);
    lv_obj_set_style_local_text_letter_space(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_text_color(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_label_set_align(label1, LV_LABEL_ALIGN_CENTER);
    lv_label_set_long_mode(label1, LV_LABEL_LONG_SROLL_CIRC);
    lv_obj_set_width(label1, obj_w * 3 / 4);
    char str[20];
    hal_rtc_t time;
    seconds_to_time(nv->end_time, &time);
    if(LV_BIDI_DIR_RTL == lv_obj_get_base_dir(cont)) {
        lv_obj_set_base_dir(label1, LV_BIDI_DIR_LTR);
        if(12 >= time.tm_hour) {
            snprintf(str, 20, "AM %02d:%02d %02d/%02d",
                     time.tm_hour, time.tm_min, time.tm_mon, time.tm_mday);
        } else {
            snprintf(str, 20, "PM %02d:%02d %02d/%02d",
                     time.tm_hour - 12, time.tm_min, time.tm_mon, time.tm_mday);
        }
    } else {
        if(12 >= time.tm_hour) {
            snprintf(str, 20, "%02d/%02d %02d:%02d AM",
                     time.tm_mon, time.tm_mday, time.tm_hour, time.tm_min);
        } else {
            snprintf(str, 20, "%02d/%02d %02d:%02d PM",
                     time.tm_mon, time.tm_mday, time.tm_hour - 12, time.tm_min);
        }
    }
    lv_label_set_text(label1, str);
    lv_coord_t top_y = lv_obj_get_y(img_bg) + img_bg_size;
    lv_coord_t inner
        = (bottom_y - top_y - lv_obj_get_height(label) - lv_obj_get_height(label1)) / 2;
    lv_obj_align(label, cont, LV_ALIGN_IN_TOP_MID, 0, top_y + inner);
    lv_obj_align(label1, label, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
}

static void exercise_record_scrl_drag_end_cb(lv_obj_t * scrl, lv_event_t event)
{
    if(LV_EVENT_DRAG_END != event) return;

    lv_coord_t pos_y = lv_obj_get_y(scrl);
    uint8_t index = -pos_y / LV_VER_RES;
    if((LV_VER_RES / 2) < (-pos_y % LV_VER_RES)) index++;
    lv_obj_t * cont = lv_obj_get_child_back(scrl, NULL);
    lv_obj_t * page = lv_obj_get_parent(scrl);
    while(cont) {
        if(0 == index) {
            lv_page_focus(page, cont, false);
            break;
        }
        index--;
        cont = lv_obj_get_child_back(scrl, cont);
    }
}

static lv_obj_t * exercise_record_sport_create(uint8_t index)
{
    printf("%s\n", __FUNCTION__);

    lv_watch_activity_ext_t activity_ext = {};
    activity_ext.actId = ACT_ID_EXERCISE_RECORD_SPORT;
    activity_ext.create = NULL;
    activity_ext.prepare_destory = exercise_record_prepare_destroy;
    lv_obj_t * activity_obj = lv_watch_creat_activity_obj(&activity_ext);
    LV_ASSERT_MEM(activity_obj);

    lv_obj_t * obj = lv_watch_obj_create(activity_obj);
    LV_ASSERT_MEM(obj);

    lv_coord_t obj_w = lv_obj_get_width(obj);
    lv_coord_t obj_h = lv_obj_get_height(obj);
    lv_coord_t arc_w = obj_h / 20;

    lv_obj_t * page = lv_page_create(obj, NULL);
    lv_page_set_scrollbar_mode(page, LV_SCROLLBAR_MODE_OFF);
    lv_obj_set_style_local_bg_opa(page, LV_PAGE_PART_BG, LV_STATE_DEFAULT, LV_OPA_COVER);
    lv_obj_set_style_local_bg_color(page, LV_PAGE_PART_BG, LV_STATE_DEFAULT, LV_COLOR_BLACK);
    lv_obj_set_style_local_pad_inner(page, LV_PAGE_PART_BG, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_pad_top(page, LV_PAGE_PART_BG, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_pad_bottom(page, LV_PAGE_PART_BG, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_pad_left(page, LV_PAGE_PART_BG, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_pad_right(page, LV_PAGE_PART_BG, LV_STATE_DEFAULT, 0);
    lv_obj_set_size(page, obj_w, obj_h);
    lv_obj_align(page, obj, LV_ALIGN_CENTER, 0, 0);
    lv_obj_t * scrl = lv_page_get_scrollable(page);
    lv_obj_set_style_local_pad_inner(scrl, LV_PAGE_PART_BG, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_pad_top(scrl, LV_PAGE_PART_BG, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_pad_bottom(scrl, LV_PAGE_PART_BG, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_pad_left(scrl, LV_PAGE_PART_BG, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_pad_right(scrl, LV_PAGE_PART_BG, LV_STATE_DEFAULT, 0);
    lv_cont_set_layout(scrl, LV_LAYOUT_COLUMN_MID);
    lv_obj_set_event_cb(scrl, exercise_record_scrl_drag_end_cb);
    lv_obj_set_drag_throw(scrl, false);
    lv_watch_obj_add_element(page);
    lv_watch_obj_add_element(scrl);

    uint8_t * data = exercise_record_read_nv(index, NULL);
    LV_ASSERT_MEM(data);

    exercise_record_base_t * nv = (exercise_record_base_t *)data;
    printf("%s: index %d, sport type %d\n", __FUNCTION__, index, nv->sport_type);

    exercise_record_add_homepage(page, nv);

    lv_bidi_dir_t dir = lv_obj_get_base_dir(page);

    exercise_record_content_t content = {};
    uint32_t offset = sizeof(exercise_record_base_t);
    exercise_record_synthesis_t * synthesis = (exercise_record_synthesis_t *)(&data[offset]);
    content.title_txt_id = WATCH_TEXT_ID_SYNTHESIS;
    uint32_t i = 0;
    if(0 != synthesis->total_disctance) {
        content.info[i].txt_id = WATCH_TEXT_ID_TOTAL_DISTANCE;
        exercise_record_get_str(synthesis->total_disctance / 10, 2, content.info[i].str, 20, false);
        strcat(content.info[i].str, " ");
        strcat(content.info[i].str, (char *)lv_lang_get_text(WATCH_TEXT_ID_KILOMETRE));
        content.info[i].img_src = ICON_EXERCISE_DISTANCE;
        i++;
    }
    content.info[i].txt_id = WATCH_TEXT_ID_EXERCISE_TIME;
    if(LV_BIDI_DIR_RTL == dir) {
        snprintf(content.info[i].str, 20, "%02d:%02d:%02d", synthesis->sport_time % 60,
                 (synthesis->sport_time / 60) % 60, synthesis->sport_time / 3600);
    } else {
        snprintf(content.info[i].str, 20, "%02d:%02d:%02d", synthesis->sport_time / 3600,
                 (synthesis->sport_time / 60) % 60, synthesis->sport_time % 60);
    }
    content.info[i].img_src = ICON_EXERCISE_TIME;
    i++;
    content.info[i].txt_id = WATCH_TEXT_ID_CALORIE;
    snprintf(content.info[i].str, 20, "%d %s", synthesis->calorie,
             (char *)lv_lang_get_text(WATCH_TEXT_ID_KILOCALORIE));
    content.info[i].img_src = ICON_EXERCISE_CALORIE;
    exercise_record_add_content(page, &content);
    offset += sizeof(exercise_record_synthesis_t);

    uint32_t j;
    uint32_t value;
    exercise_record_speed_t * speed = NULL;
    exercise_record_pace_t * pace = NULL;
    exercise_record_step_t * step;
    exercise_record_height_t * height;
    exercise_record_hr_curve_t * hr_curve;
    exercise_record_hr_zone_t * hr_zone;
    exercise_record_speed_curve_t * speed_curve;
    exercise_record_pace_curve_t * pace_curve;
    exercise_record_stride_freq_curve_t * stride_freq_curve;
    exercise_record_altitude_curve_t * altitude_curve;
    exercise_record_training_effect_t * training_effect;
    exercise_record_recovery_time_t * recovery_time;
    exercise_record_vo2max_t * vo2max;
    lv_coord_t y_array[EXERCISE_RECORD_CURVE_NODE_NUM] = {};
    for(i = 0;i < EXERCISE_RECORD_ITEM_TYPE_MAX;i++) {
        if(0 == (nv->bitmap & (1 << i))) continue;
        switch(i) {
        case EXERCISE_RECORD_ITEM_TYPE_SPEED:
            speed = (exercise_record_speed_t *)(&data[offset]);
            content.title_txt_id = WATCH_TEXT_ID_SPEED;
            content.info[0].txt_id = WATCH_TEXT_ID_TOP_SPEED;
            snprintf(content.info[0].str, 20, "%d.%02d %s",
                     speed->top_speed / 1000, (speed->top_speed % 1000) / 10,
                     (char *)lv_lang_get_text(WATCH_TEXT_ID_KM_PER_HOUR));
            content.info[0].img_src = ICON_EXERCISE_RECORD_SPEED;
            content.info[1].txt_id = WATCH_TEXT_ID_AVER_SPEED;
            snprintf(content.info[1].str, 20, "%d.%02d %s",
                     speed->aver_speed / 1000, (speed->aver_speed % 1000) / 10,
                     (char *)lv_lang_get_text(WATCH_TEXT_ID_KM_PER_HOUR));
            content.info[1].img_src = ICON_EXERCISE_RECORD_SPEED;
            content.info[2].txt_id = WATCH_TEXT_ID_AVERAGE_PACE;
            if(LV_BIDI_DIR_RTL == dir) {
                snprintf(content.info[2].str, 20, "\"%02d'%d /%s", speed->aver_pace % 60,
                         speed->aver_pace / 60, (char *)lv_lang_get_text(WATCH_TEXT_ID_KILOMETRE));
            } else {
                snprintf(content.info[2].str, 20, "%d'%02d\" /%s", speed->aver_pace / 60,
                         speed->aver_pace % 60, (char *)lv_lang_get_text(WATCH_TEXT_ID_KILOMETRE));
            }
            content.info[2].img_src = ICON_EXERCISE_PACE;
            exercise_record_add_content(page, &content);
            offset += sizeof(exercise_record_speed_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_PACE:
            pace = (exercise_record_pace_t *)(&data[offset]);
            content.title_txt_id = WATCH_TEXT_ID_SPEED;
            content.info[0].txt_id = WATCH_TEXT_ID_TOP_PACE;
            if(LV_BIDI_DIR_RTL == dir) {
                snprintf(content.info[0].str, 20, "\"%02d'%d /%s", pace->top_pace % 60,
                         pace->top_pace / 60, (char *)lv_lang_get_text(WATCH_TEXT_ID_KILOMETRE));
            } else {
                snprintf(content.info[0].str, 20, "%d'%02d\" /%s", pace->top_pace / 60,
                         pace->top_pace % 60, (char *)lv_lang_get_text(WATCH_TEXT_ID_KILOMETRE));
            }
            content.info[0].img_src = ICON_EXERCISE_PACE;
            content.info[1].txt_id = WATCH_TEXT_ID_AVER_SPEED;
            snprintf(content.info[1].str, 20, "%d.%02d %s",
                     pace->aver_speed / 1000, (pace->aver_speed % 1000) / 10,
                     (char *)lv_lang_get_text(WATCH_TEXT_ID_KM_PER_HOUR));
            content.info[1].img_src = ICON_EXERCISE_RECORD_SPEED;
            content.info[2].txt_id = WATCH_TEXT_ID_AVERAGE_PACE;
            if(LV_BIDI_DIR_RTL == dir) {
                snprintf(content.info[2].str, 20, "\"%02d'%d /%s", pace->aver_pace % 60,
                         pace->aver_pace / 60, (char *)lv_lang_get_text(WATCH_TEXT_ID_KILOMETRE));
            } else {
                snprintf(content.info[2].str, 20, "%d'%02d\" /%s", pace->aver_pace / 60,
                         pace->aver_pace % 60, (char *)lv_lang_get_text(WATCH_TEXT_ID_KILOMETRE));
            }
            content.info[2].img_src = ICON_EXERCISE_PACE;
            exercise_record_add_content(page, &content);
            offset += sizeof(exercise_record_pace_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_STEP:
            step = (exercise_record_step_t *)(&data[offset]);
            content.title_txt_id = WATCH_TEXT_ID_STEP;
            content.info[0].txt_id = WATCH_TEXT_ID_STEP_NUMBER;
            if(1 < step->step_number) {
                snprintf(content.info[0].str, 20, "%d %s", step->step_number,
                         (char *)lv_lang_get_text(WATCH_TEXT_ID_ACT_STEPS));
            } else {
                snprintf(content.info[0].str, 20, "%d %s", step->step_number,
                         (char *)lv_lang_get_text(WATCH_TEXT_ID_ACT_STEP));
            }
            content.info[0].img_src = ICON_EXERCISE_RECORD_STEP_NUM;
            content.info[1].txt_id = WATCH_TEXT_ID_AVER_STRIDE_FREQ;
            snprintf(content.info[1].str, 20, "%d %s", step->aver_stride_freq,
                     (char *)lv_lang_get_text(WATCH_TEXT_ID_STEP_PER_MIN));
            content.info[1].img_src = ICON_EXERCISE_RECORD_STRIDE_FREQ;
            content.info[2].txt_id = WATCH_TEXT_ID_AVERAGE_STRIDE;
            snprintf(content.info[2].str, 20, "%d %s", step->aver_stride,
                     (char *)lv_lang_get_text(WATCH_TEXT_ID_CM));
            content.info[2].img_src = ICON_EXERCISE_RECORD_STRIDE;
            exercise_record_add_content(page, &content);
            offset += sizeof(exercise_record_step_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_HEIGHT:
            height = (exercise_record_height_t *)(&data[offset]);
            content.title_txt_id = WATCH_TEXT_ID_HEIGHT;
            content.info[0].txt_id = WATCH_TEXT_ID_CUMULATIVELY_CLIMB;
            snprintf(content.info[0].str, 20, "%d.%d %s", height->climb_height / 10,
                     height->climb_height % 10, (char *)lv_lang_get_text(WATCH_TEXT_ID_METER));
            content.info[0].img_src = ICON_EXERCISE_RECORD_CLIMB;
            content.info[1].txt_id = WATCH_TEXT_IDE_CUMULATIVE_DECLINE;
            snprintf(content.info[1].str, 20, "%d.%d %s", height->decline_height / 10,
                     height->decline_height % 10, (char *)lv_lang_get_text(WATCH_TEXT_ID_METER));
            content.info[1].img_src = ICON_EXERCISE_RECORD_DECLINE;
            content.info[2].txt_id = WATCH_TEXT_ID_HIGHEST_ALTITUDE;
            snprintf(content.info[2].str, 20, "%d.%d %s", height->highest_altitude / 10,
                     height->highest_altitude % 10, (char *)lv_lang_get_text(WATCH_TEXT_ID_METER));
            content.info[2].img_src = ICON_EXERCISE_RECORD_ALTITUDE;
            exercise_record_add_content(page, &content);
            offset += sizeof(exercise_record_height_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_HR_CURVE:
            hr_curve = (exercise_record_hr_curve_t *)(&data[offset]);
            for(j = 0;j < EXERCISE_RECORD_CURVE_NODE_NUM;j++) {
                if(LV_BIDI_DIR_RTL == dir) {
                    y_array[EXERCISE_RECORD_CURVE_NODE_NUM - 1 - j] = hr_curve->hr[j];
                } else {
                    y_array[j] = hr_curve->hr[j];
                }
            }
            exercise_record_add_curve(page, synthesis->sport_time, WATCH_TEXT_ID_HEART_RATE_CURVE,
                                      hr_curve->min_hr, hr_curve->max_hr, hr_curve->aver_hr,
                                      LV_COLOR_RED, y_array, EXERCISE_RECORD_CURVE_NODE_NUM,
                                      WATCH_TEXT_ID_BEATS_PER_MIN, 0, false);
            offset += sizeof(exercise_record_hr_curve_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_HR_ZONE:
            hr_zone = (exercise_record_hr_zone_t *)(&data[offset]);
            exercise_record_add_hr_zone(page, hr_zone);
            offset += sizeof(exercise_record_hr_zone_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_SPEED_CURVE:
            speed_curve = (exercise_record_speed_curve_t *)(&data[offset]);
            for(j = 0;j < EXERCISE_RECORD_CURVE_NODE_NUM;j++) {
                if(LV_BIDI_DIR_RTL == dir) {
                    y_array[EXERCISE_RECORD_CURVE_NODE_NUM - 1 - j] = speed_curve->speed[j];
                } else {
                    y_array[j] = speed_curve->speed[j];
                }
            }
            exercise_record_add_curve(page, synthesis->sport_time, WATCH_TEXT_ID_SPEED_CURVE,
                                      0, speed->top_speed / 10, speed->aver_speed / 10,
                                      LV_COLOR_MAKE(0x00, 0xFF, 0x40), y_array,
                                      EXERCISE_RECORD_CURVE_NODE_NUM, WATCH_TEXT_ID_KM_PER_HOUR,
                                      2, false);
            offset += sizeof(exercise_record_speed_curve_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_PACE_CURVE:
            pace_curve = (exercise_record_pace_curve_t *)(&data[offset]);
            value = 0;
            for(j = 0;j < EXERCISE_RECORD_CURVE_NODE_NUM;j++) {
                if(LV_BIDI_DIR_RTL == dir) {
                    y_array[EXERCISE_RECORD_CURVE_NODE_NUM - 1 - j] = pace_curve->pace[j];
                } else {
                    y_array[j] = pace_curve->pace[j];
                }
                if(pace_curve->pace[j] > value) {
                    value = pace_curve->pace[j];
                }
            }
            exercise_record_add_curve(page, synthesis->sport_time, WATCH_TEXT_ID_PACE_CURVE,
                                      pace_curve->max_pace, value, pace_curve->aver_pace,
                                      LV_COLOR_MAKE(0xD0, 0x50, 0x00), y_array,
                                      EXERCISE_RECORD_CURVE_NODE_NUM, WATCH_TEXT_ID_PER_KM, 0, true);
            offset += sizeof(exercise_record_pace_curve_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_STRIDE_FREQ_CURVE:
            stride_freq_curve = (exercise_record_stride_freq_curve_t *)(&data[offset]);
            for(j = 0;j < EXERCISE_RECORD_CURVE_NODE_NUM;j++) {
                if(LV_BIDI_DIR_RTL == dir) {
                    y_array[EXERCISE_RECORD_CURVE_NODE_NUM - 1 - j] = stride_freq_curve->freq[j];
                } else {
                    y_array[j] = stride_freq_curve->freq[j];
                }
            }
            exercise_record_add_curve(page, synthesis->sport_time, WATCH_TEXT_ID_STRIDE_FREQ_CURVE,
                                      stride_freq_curve->min_freq, stride_freq_curve->max_freq,
                                      stride_freq_curve->aver_freq, LV_COLOR_YELLOW, y_array,
                                      EXERCISE_RECORD_CURVE_NODE_NUM, WATCH_TEXT_ID_STEP_PER_MIN,
                                      0, false);
            offset += sizeof(exercise_record_stride_freq_curve_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_ALTITUDE_CURVE:            
            altitude_curve = (exercise_record_altitude_curve_t *)(&data[offset]);
            for(j = 0;j < EXERCISE_RECORD_CURVE_NODE_NUM;j++) {
                if(LV_BIDI_DIR_RTL == dir) {
                    y_array[EXERCISE_RECORD_CURVE_NODE_NUM - 1 - j] = altitude_curve->height[j];
                } else {
                    y_array[j] = altitude_curve->height[j];
                }
            }
            exercise_record_add_curve(page, synthesis->sport_time, WATCH_TEXT_ID_ALTITUDE_CURVE,
                                      altitude_curve->min_altitude, altitude_curve->max_altitude,
                                      altitude_curve->aver_altitude, LV_COLOR_MAKE(0x00, 0xEC, 0xEC),
                                      y_array, EXERCISE_RECORD_CURVE_NODE_NUM, WATCH_TEXT_ID_METER,
                                      1, false);
            offset += sizeof(exercise_record_altitude_curve_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_TRAINING_EFFECT:
            training_effect = (exercise_record_training_effect_t *)(&data[offset]);
            exercise_record_add_training_effect(page, arc_w, training_effect);
            offset += sizeof(exercise_record_training_effect_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_RECOVERY_TIME:
            recovery_time = (exercise_record_recovery_time_t *)(&data[offset]);
            exercise_record_add_recovery_time(page, arc_w, recovery_time);
            offset += sizeof(exercise_record_recovery_time_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_VO2MAX:
            vo2max = (exercise_record_vo2max_t *)(&data[offset]);
            exercise_record_add_vo2max(page, arc_w, vo2max);
            offset += sizeof(exercise_record_vo2max_t);
            break;
        }
    }

    exercise_record_add_exit(page);

    Hal_Mem_Free(data);

    return(obj);
}

static void exercise_record_btn_cb(lv_obj_t * btn, lv_event_t event)
{
    if(LV_EVENT_CLICKED != event) return;

    uint8_t index = lv_watch_get_free_num(btn) + 1;

    exercise_record_sport_create(index);
}

static lv_obj_t * exercise_record_add_btn(lv_obj_t * list, lv_coord_t btn_h, lv_event_cb_t cb)
{
    lv_obj_t * btn = lv_list_add_btn(list, NULL, NULL);
    lv_obj_add_style(btn, LV_BTN_PART_MAIN, &lv_style_transp_tight);
    lv_btn_set_fit2(btn, LV_FIT_NONE, LV_FIT_NONE);
    lv_btn_set_layout(btn, LV_LAYOUT_OFF);
    lv_obj_set_style_local_bg_opa(btn, LV_BTN_PART_MAIN, LV_STATE_PRESSED, LV_OPA_0);
    lv_obj_set_size(btn, lv_obj_get_width(list), btn_h);
    lv_obj_set_event_cb(btn, cb);
    lv_watch_obj_add_element(btn);

    return(btn);
}

static lv_obj_t * exercise_record_add_record_btn(lv_obj_t * list, lv_coord_t btn_h,
                                                 lv_coord_t font_h, exercise_record_nv_data_t * nv)
{
    lv_obj_t * btn = exercise_record_add_btn(list, btn_h, exercise_record_btn_cb);
    lv_bidi_dir_t dir = lv_obj_get_base_dir(btn);

    char str[10];
    uint16_t txt_id;
    if(EXERCISE_RECORD_DATA_TYPE_DISTANCE == nv->data_type) {
        exercise_record_get_str(nv->data / 10, 2, str, 10, false);
        txt_id = WATCH_TEXT_ID_KILOMETRE;
    } else {
        snprintf(str, 10, "%d", nv->data);
        txt_id = WATCH_TEXT_ID_KILOCALORIE;
    }

    lv_obj_t * label = lv_label_create(btn, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN,
                                             LV_STATE_DEFAULT, 0);
    lv_label_set_text(label, str);

    lv_obj_t * label1 = lv_label_create(btn, NULL);
    lv_obj_set_style_local_text_font(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_obj_set_style_local_text_letter_space(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_text_color(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_label_set_text_id(label1, txt_id);

    lv_coord_t label_w = lv_obj_get_width(label);
    lv_coord_t label1_w = lv_obj_get_width(label1);
    lv_coord_t inner = font_h / 4;
    lv_coord_t btn_w = lv_obj_get_width(btn);
    if(LV_BIDI_DIR_RTL == dir) {
        lv_obj_align(label, btn, LV_ALIGN_IN_RIGHT_MID,
                     -(btn_w - label_w - label1_w - inner) / 2, -lv_obj_get_height(label) / 2);
        lv_obj_align(label1, label, LV_ALIGN_OUT_LEFT_MID, -inner, 0);
    } else {
        lv_obj_align(label, btn, LV_ALIGN_IN_LEFT_MID,
                     (btn_w - label_w - label1_w - inner) / 2, -lv_obj_get_height(label) / 2);
        lv_obj_align(label1, label, LV_ALIGN_OUT_RIGHT_MID, inner, 0);
    }

    label1 = lv_label_create(btn, label1);
    lv_obj_set_base_dir(label1, LV_BIDI_DIR_LTR);
    hal_rtc_t time;
    seconds_to_time(nv->end_time, &time);
    snprintf(str, 10, "%02d/%02d", time.tm_mon, time.tm_mday);
    lv_label_set_text(label1, str);
    lv_obj_align(label1, btn, LV_ALIGN_CENTER, 0, lv_obj_get_height(label1) / 2);

    void * img_src;
    lv_color_t img_bg_color;
    exercise_record_get_sport_info(nv->sport_type, NULL, &img_src, &img_bg_color);

    lv_obj_t * img_bg = lv_cont_create(btn, NULL);
    lv_obj_set_click(img_bg, false);
    lv_obj_set_style_local_bg_opa(img_bg, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_COVER);
    lv_obj_set_style_local_bg_color(img_bg, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, img_bg_color);
    lv_obj_set_style_local_radius(img_bg, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
    lv_coord_t bg_size = 2 * font_h;
    lv_obj_set_size(img_bg, bg_size, bg_size);
    if(LV_BIDI_DIR_RTL == dir) {
        lv_obj_align(img_bg, btn, LV_ALIGN_IN_RIGHT_MID, -btn_w / 4 + bg_size, 0);
    } else {
        lv_obj_align(img_bg, btn, LV_ALIGN_IN_LEFT_MID, btn_w / 4 - bg_size, 0);
    }

    lv_obj_t * img = lv_img_create(img_bg, NULL);
    lv_img_set_src(img, img_src);
    lv_img_set_zoom(img, LV_IMG_ZOOM_NONE * bg_size / lv_obj_get_height(img));
    lv_obj_align(img, img_bg, LV_ALIGN_CENTER, 0, 0);

    label = lv_label_create(btn, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_BIG);
    lv_label_set_text(label, LV_SYMBOL_RIGHT);
    if(LV_BIDI_DIR_RTL == dir) {
        lv_obj_align(label, btn, LV_ALIGN_IN_LEFT_MID,
                     -inner - lv_obj_get_width(label) + btn_w / 4, 2);
    } else {
        lv_obj_align(label, btn, LV_ALIGN_IN_RIGHT_MID,
                     inner + lv_obj_get_width(label) - btn_w / 4, 2);
    }

    return(btn);
}

static lv_obj_t * exercise_record_main_create(lv_obj_t * activity_obj)
{
    printf("%s\n", __FUNCTION__);
    if(NULL == activity_obj) {
        lv_watch_activity_ext_t activity_ext = {};
        activity_ext.actId = ACT_ID_EXERCISE_RECORD;
        activity_ext.create = exercise_record_main_create;
        activity_ext.prepare_destory = exercise_record_prepare_destroy;
        activity_obj = lv_watch_creat_activity_obj(&activity_ext);
        LV_ASSERT_MEM(activity_obj);
    }
    lv_obj_t * obj = lv_watch_obj_create(activity_obj);
    LV_ASSERT_MEM(obj);

    lv_obj_t * list = lv_list_create(obj, NULL);
    lv_list_set_scrollbar_mode(list, LV_SCROLLBAR_MODE_OFF);
    lv_obj_set_style_local_bg_opa(list, LV_LIST_PART_BG, LV_STATE_DEFAULT, LV_OPA_COVER);
    lv_obj_set_style_local_bg_color(list, LV_LIST_PART_BG, LV_STATE_DEFAULT, LV_COLOR_BLACK);
    lv_obj_set_size(list, lv_obj_get_width(obj), lv_obj_get_height(obj));
    lv_coord_t font_h = lv_font_get_line_height(LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_coord_t list_btn_pad_ver = 2;
    lv_coord_t list_btn_h = 2 * (list_btn_pad_ver + font_h);
    uint8_t btn_num = 4;
    lv_coord_t list_pad_inner = (lv_obj_get_height(obj) - (btn_num * list_btn_h)) / btn_num;
    lv_obj_add_style(list, LV_LIST_PART_SCROLLABLE, &lv_style_transp_tight);
    lv_obj_set_style_local_pad_top(list, LV_LIST_PART_SCROLLABLE, LV_STATE_DEFAULT,
                                   list_pad_inner / 2);
    lv_obj_set_style_local_pad_bottom(list, LV_LIST_PART_SCROLLABLE, LV_STATE_DEFAULT,
                                      list_pad_inner / 2);
    lv_obj_set_style_local_pad_inner(list, LV_LIST_PART_SCROLLABLE, LV_STATE_DEFAULT,
                                     list_pad_inner);
    lv_obj_align(list, obj, LV_ALIGN_CENTER, 0, 0);
    lv_watch_obj_add_element(list);
    lv_watch_obj_add_element(lv_page_get_scrollable(list));

    exercise_record_nv_t nv = {};
    exercise_record_read_nv(0, (uint8_t *)(&nv));

    lv_obj_t * btn = exercise_record_add_btn(list, list_btn_h, NULL);

    lv_obj_t * label = lv_label_create(btn, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
    lv_label_set_long_mode(label, LV_LABEL_LONG_SROLL_CIRC);
    lv_obj_set_width(label, lv_obj_get_width(btn) * 2 / 3);
    lv_label_set_text_id(label, WATCH_TEXT_ID_EXERCISE_RECORDS);
    lv_obj_align(label, btn, LV_ALIGN_CENTER, 0, 0);

    if(0 == nv.data[0].end_time) {
        label = lv_label_create(obj, NULL);
        lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                         LV_THEME_WATCH_NIGHT_FONT_BIG);
        lv_label_set_text_id(label, WATCH_TEXT_ID_EMPTY);
        lv_obj_align(label, obj, LV_ALIGN_CENTER, 0, 0);
    } else {
        uint8_t index = 0;
        for(uint8_t i = 0;i < EXERCISE_RECORD_MAX_NUM;i++) {
            if(nv.data[i].end_time < nv.data[index].end_time) {
                break;
            } else {
                index = i;
            }
        }
        for(int8_t i = index;i >= 0;i--) {
            btn = exercise_record_add_record_btn(list, list_btn_h, font_h, &nv.data[i]);
            lv_watch_set_free_num(btn, i);
        }
        if(0 < nv.data[EXERCISE_RECORD_MAX_NUM - 1].end_time) {
            for(int8_t i = EXERCISE_RECORD_MAX_NUM - 1;i > index;i--) {
                btn = exercise_record_add_record_btn(list, list_btn_h, font_h, &nv.data[i]);
                lv_watch_set_free_num(btn, i);
            }
        }

        exercise_record_add_btn(list, list_btn_h, NULL);
    }

    return(obj);
}

static void exercise_record_curve_fitting_uint8(uint8_t * src)
{
    double mypower = 1.122018;//pow(10, 0.05);
    uint8_t dst[EXERCISE_RECORD_CURVE_NODE_NUM];
	Hal_Mem_Copy(dst, src, EXERCISE_RECORD_CURVE_NODE_NUM * sizeof(uint8_t));

	for(int32_t i = (int32_t)mypower + 1;i < EXERCISE_RECORD_CURVE_NODE_NUM ;i++) {
		int32_t index_start = (int32_t)(((double)i / mypower) + (double)1.0);
		int32_t index_end = (int32_t)((double)i * mypower) + 1;
		index_end = (index_end >= (EXERCISE_RECORD_CURVE_NODE_NUM - 1)) ?
            (EXERCISE_RECORD_CURVE_NODE_NUM - 1) : index_end;
        int32_t sum = 0;
        for(uint32_t j = index_start;j <= index_end;j++) {
            sum += dst[j];
        }
		src[i] = sum / (index_end - index_start + 1);
	}
}

static void exercise_record_curve_fitting_uint16(uint16_t * src)
{
	double mypower = 1.122018;//pow(10, 0.05);
    uint16_t dst[EXERCISE_RECORD_CURVE_NODE_NUM];
	Hal_Mem_Copy(dst, src, EXERCISE_RECORD_CURVE_NODE_NUM * sizeof(uint16_t));	

	for(int32_t i = (int32_t)mypower + 1;i < EXERCISE_RECORD_CURVE_NODE_NUM ;i++) {
		int32_t index_start = (int32_t)(((double)i / mypower) + (double)1.0);
		int32_t index_end = (int32_t)((double)i * mypower) + 1;
		index_end = (index_end >= (EXERCISE_RECORD_CURVE_NODE_NUM - 1)) ?
            (EXERCISE_RECORD_CURVE_NODE_NUM - 1) : index_end;
        uint32_t sum = 0;
        for(uint32_t j = index_start;j <= index_end;j++) {
            sum += dst[j];
        }
		src[i] = sum / (index_end - index_start + 1);
	}
}

/**********************
 *   GLOBAL FUNCTIONS
 ***********************/
void exercise_record_create_event_cb(lv_obj_t * btn, lv_event_t event)
{
    (void)btn;
    if(LV_EVENT_CLICKED == event) {
        lv_watch_png_cache_all_free();
        lv_obj_t * obj = exercise_record_main_create(NULL);
        LV_ASSERT_MEM(obj);
    }
}

void exercise_record_sport_data_ind(exercise_record_t * record)
{
    if(NULL == record) {
        printf("%s: warning, record is NULL\n", __FUNCTION__);
        return;
    }

    exercise_record_nv_t nv = {};
    exercise_record_read_nv(0, (uint8_t *)(&nv));
    uint8_t index = 0;
    // search the empty or oldest item
    for(uint8_t i = 0;i < EXERCISE_RECORD_MAX_NUM;i++) {
        if(0 == nv.data[i].end_time) {
            index = i;
            break;
        }
        if(nv.data[i].end_time < nv.data[index].end_time) {
            index = i;
        }
    }
    if(0 < nv.data[index].end_time) {
        exercise_record_delete_nv(index);
    }
    exercise_record_nv_data_t * item = &nv.data[index];
    item->sport_type = record->base.sport_type;
    if(0 != record->synthesis.total_disctance) {
        item->data_type = EXERCISE_RECORD_DATA_TYPE_DISTANCE;
        item->data = record->synthesis.total_disctance;
    } else {
        item->data_type = EXERCISE_RECORD_DATA_TYPE_CALORIE;
        item->data = record->synthesis.calorie;
    }
    item->end_time = record->base.end_time;
    exercise_record_write_nv(0, sizeof(exercise_record_nv_t), (void *)(&nv));

    uint32_t size = sizeof(exercise_record_base_t) + sizeof(exercise_record_synthesis_t);
    for(uint8_t i = 0;i < EXERCISE_RECORD_ITEM_TYPE_MAX;i++) {
        if(0 == (record->base.bitmap & (1 << i))) continue;
        switch(i) {
        case EXERCISE_RECORD_ITEM_TYPE_SPEED:
            size += sizeof(exercise_record_speed_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_PACE:
            size += sizeof(exercise_record_pace_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_STEP:
            size += sizeof(exercise_record_step_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_HEIGHT:
            size += sizeof(exercise_record_height_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_HR_CURVE:
            size += sizeof(exercise_record_hr_curve_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_HR_ZONE:
            size += sizeof(exercise_record_hr_zone_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_SPEED_CURVE:
            size += sizeof(exercise_record_speed_curve_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_PACE_CURVE:
            size += sizeof(exercise_record_pace_curve_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_STRIDE_FREQ_CURVE:
            size += sizeof(exercise_record_stride_freq_curve_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_ALTITUDE_CURVE:
            size += sizeof(exercise_record_altitude_curve_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_TRAINING_EFFECT:
            size += sizeof(exercise_record_training_effect_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_RECOVERY_TIME:
            size += sizeof(exercise_record_recovery_time_t);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_VO2MAX:
            size += sizeof(exercise_record_vo2max_t);
            break;
        }
    }
    uint8_t * data = Hal_Mem_Alloc(size);
    Hal_Mem_Set(data, 0, size);
    size = sizeof(exercise_record_base_t);
    Hal_Mem_Copy(data, &record->base, size);
    uint32_t offset = size;
    size = sizeof(exercise_record_synthesis_t);
    Hal_Mem_Copy(&data[offset], &record->synthesis, size);
    offset += size;
    for(uint8_t i = 0;i < EXERCISE_RECORD_ITEM_TYPE_MAX;i++) {
        if(0 == (record->base.bitmap & (1 << i))) continue;
        switch(i) {
        case EXERCISE_RECORD_ITEM_TYPE_SPEED:
            size = sizeof(exercise_record_speed_t);
            Hal_Mem_Copy(&data[offset], &record->speed, size);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_PACE:
            size = sizeof(exercise_record_pace_t);
            Hal_Mem_Copy(&data[offset], &record->pace, size);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_STEP:
            size = sizeof(exercise_record_step_t);
            Hal_Mem_Copy(&data[offset], &record->step, size);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_HEIGHT:
            size = sizeof(exercise_record_height_t);
            Hal_Mem_Copy(&data[offset], &record->height, size);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_HR_CURVE:
            size = sizeof(exercise_record_hr_curve_t);
            exercise_record_curve_fitting_uint8(record->hr_curve.hr);
            Hal_Mem_Copy(&data[offset], &record->hr_curve, size);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_HR_ZONE:
            size = sizeof(exercise_record_hr_zone_t);
            Hal_Mem_Copy(&data[offset], &record->hr_zone, size);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_SPEED_CURVE:
            size = sizeof(exercise_record_speed_curve_t);
            exercise_record_curve_fitting_uint16(record->speed_curve.speed);
            Hal_Mem_Copy(&data[offset], &record->speed_curve, size);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_PACE_CURVE:
            size = sizeof(exercise_record_pace_curve_t);
            exercise_record_curve_fitting_uint16(record->pace_curve.pace);
            Hal_Mem_Copy(&data[offset], &record->pace_curve, size);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_STRIDE_FREQ_CURVE:
            size = sizeof(exercise_record_stride_freq_curve_t);
            exercise_record_curve_fitting_uint8(record->stride_freq_curve.freq);
            Hal_Mem_Copy(&data[offset], &record->stride_freq_curve, size);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_ALTITUDE_CURVE:
            size = sizeof(exercise_record_altitude_curve_t);
            exercise_record_curve_fitting_uint16(record->altitude_curve.height);
            Hal_Mem_Copy(&data[offset], &record->altitude_curve, size);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_TRAINING_EFFECT:
            size = sizeof(exercise_record_training_effect_t);
            Hal_Mem_Copy(&data[offset], &record->training_effect, size);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_RECOVERY_TIME:
            size = sizeof(exercise_record_recovery_time_t);
            Hal_Mem_Copy(&data[offset], &record->recovery_time, size);
            break;
        case EXERCISE_RECORD_ITEM_TYPE_VO2MAX:
            size = sizeof(exercise_record_vo2max_t);
            Hal_Mem_Copy(&data[offset], &record->vo2max, size);
            break;
        }
        offset += size;
    }
    exercise_record_write_nv(index + 1, offset, data);
}

#endif /*USE_LV_WATCH_EXERCISE_RECORD*/
